package guowei.gulistorage.storage.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.rabbitmq.client.Channel;
import guowei.common.to.es.WareSkuIdStocks;
import guowei.common.to.mq.OrderVo;
import guowei.common.to.mq.StockLockedTo;
import guowei.common.to.mq.TaskDetailTo;
import guowei.common.utils.R;
import guowei.gulistorage.storage.entity.WareOrderTaskDetailEntity;
import guowei.gulistorage.storage.entity.WareOrderTaskEntity;
import guowei.gulistorage.storage.exception.WareNotEnoughException;
import guowei.gulistorage.storage.feign.OrderFeign;
import guowei.gulistorage.storage.feign.UserFeign;
import guowei.gulistorage.storage.service.WareOrderTaskDetailService;
import guowei.gulistorage.storage.service.WareOrderTaskService;
import guowei.gulistorage.storage.vo.*;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import guowei.common.utils.PageUtils;
import guowei.common.utils.Query;

import guowei.gulistorage.storage.dao.WareSkuDao;
import guowei.gulistorage.storage.entity.WareSkuEntity;
import guowei.gulistorage.storage.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    private WareSkuDao wareSkuDao;

    @Autowired
    UserFeign userFeign;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    OrderFeign orderFeign;

//用于保存库存工作单，追溯商品库存锁了几件
    @Autowired
    private WareOrderTaskService wareOrderTaskService;

    @Autowired
    private WareOrderTaskDetailService wareOrderTaskDetailService;



    /**
     * 按wareId和skuId查询
     * @param params
     * @return
     */
    @Override
    public PageUtils queryPage(Map<String, Object> params) {

        String skuId = (String) params.get("skuId");
        String wareId = (String) params.get("wareId");

        QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<>();

        if (!StringUtils.isEmpty(skuId) && !"0".equals(skuId)){
            wrapper.eq("sku_id",skuId);
        }
        if (!StringUtils.isEmpty(wareId) && !"0".equals(wareId)){
            wrapper.eq("ware_id",wareId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );
        return new PageUtils(page);
    }

    @Override
    public List<WareSkuIdStocks> getAllStorages(List<Long> skuIds) {
        List<WareSkuIdStocks> collect = skuIds.stream().map(id -> {
            Long stock = wareSkuDao.getStockBySkuId(id);
            WareSkuIdStocks wareSkuIdStocks = new WareSkuIdStocks();
            wareSkuIdStocks.setSkuId(id);
            wareSkuIdStocks.setStock(stock);
            return wareSkuIdStocks;
        }).collect(Collectors.toList());
        return collect;
    }

    /**
     * 查询是否有库存，有则返回true,吴泽返回false
     * @param ids
     * @return
     */
    @Override
    public List<StockHasVo> getInfoBuSkuIds(List<Long> ids) {
        List<StockHasVo> hasVos = ids.stream().map(id -> {
            WareSkuEntity wareSku = wareSkuDao.selectOne(new QueryWrapper<WareSkuEntity>().eq("sku_id", id));
            StockHasVo stockHasVo = new StockHasVo();
            stockHasVo.setSkuId(id);
            if (wareSku == null || wareSku.getStock() <= 0){
                stockHasVo.setHasStock(false);
            }else {
                stockHasVo.setHasStock(true);
            }
            return stockHasVo;
        }).collect(Collectors.toList());
        return hasVos;
    }

    @Override
    public AddressAndFareVo getfareId(Long addrId) {
        AddressAndFareVo addressAndFareVo = new AddressAndFareVo();

        R userInfo = userFeign.getUserInfo(addrId);
        MemAddressVo memAddressVo = (MemAddressVo) userInfo.getData("memberReceiveAddress", new TypeReference<MemAddressVo>() {
        });
        String phone = memAddressVo.getPhone();
        String sub = phone.substring(phone.length() - 1, phone.length());
        BigDecimal bigDecimal = new BigDecimal(sub);
        addressAndFareVo.setAddress(memAddressVo);
        addressAndFareVo.setFare(bigDecimal);
        return addressAndFareVo;
    }

    /**
     * 锁定库存服务
     * @param lockVo
     * @return
     */
    @Override
    @Transactional
    public Boolean tryLockItems(OrderStorageLockVo lockVo) {
        /**
         * 保存库存工作单的详情
         * 追溯锁了那个商品，锁了几件
         */
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(lockVo.getOrderSn());
        wareOrderTaskService.save(wareOrderTaskEntity);

//  1.先找到当前商品在那个仓库有库存
        List<OrderItemVo> items = lockVo.getLocks();

        List<WareSkuInfo> wareSkuInfos = items.stream().map(item -> {
            WareSkuInfo wareSkuInfo = new WareSkuInfo();
            List<Long> wareIds = wareSkuDao.selectWareIdsBySkuId(item.getSkuId());
    //只要有一个商品库存不够就报异常，直接回滚
            if (wareIds == null || wareIds.size() == 0){
                throw new WareNotEnoughException(item.getSkuId());
            }
            wareSkuInfo.setWareIds(wareIds);
            wareSkuInfo.setNum(item.getSkuQuantity());
            wareSkuInfo.setSkuId(item.getSkuId());
            return wareSkuInfo;
        }).collect(Collectors.toList());

    // 若都没有库存，则报异常，某一商品没库存也是保异常
        if (wareSkuInfos == null || wareSkuInfos.size()==0){
            throw new WareNotEnoughException();
        }

//  2.找到有库存的仓库 尝试锁定库存
    //此标志为true 表示每一个仓库都成功锁定了库存
        Boolean locked = true;
        for (WareSkuInfo wareSkuInfo:wareSkuInfos) {
    //依序遍历每个仓库，直到锁定库存成功
            for (Long wareId : wareSkuInfo.wareIds) {
    //这是锁库存语句 若锁定成功就locked=true
                Integer count = wareSkuDao.lockSkuInWare(wareSkuInfo.getSkuId(), wareSkuInfo.getNum(), wareId);
                if (count == 0) {
                    locked = false;
                    throw new WareNotEnoughException(wareSkuInfo.getSkuId());
                }
//              TODO  首先保存工作单详情信息 将要告诉mq消息库存锁定成功
                WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity(null, wareSkuInfo.skuId,"", wareSkuInfo.num,wareOrderTaskEntity.getId(),wareId,1);
                wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);

                StockLockedTo stockLockedTo = new StockLockedTo();  //保存库存单锁定信息
                stockLockedTo.setId(wareOrderTaskEntity.getId());
                TaskDetailTo taskDetailTo = new TaskDetailTo();     //库存单详细信息
                BeanUtils.copyProperties(wareOrderTaskDetailEntity,taskDetailTo);
                stockLockedTo.setDetail(taskDetailTo);
                rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",stockLockedTo);
            }
        }
    //到这一步肯定都是锁定库存成功了
        return true;
    }

    @Override
    public void listenStock(StockLockedTo stockLockedTo, Message message, Channel channel) {
        TaskDetailTo detail = stockLockedTo.getDetail();
        Long detailId = detail.getId();
        WareOrderTaskDetailEntity entity = wareOrderTaskDetailService.getById(detailId);

//  如果工作单详情 的lock_status为未解锁状态才能继续下一步
        if (entity!= null && entity.getLockStatus() == 1){
            //解锁库存
            Long id = stockLockedTo.getId();
            WareOrderTaskEntity byId = wareOrderTaskService.getById(id);
            String orderSn = byId.getOrderSn();

//      查询订单状态 若订单状态是 已取消    或订单不存在就 解锁库存
            R r = orderFeign.getOrderByOrderSn(orderSn);

            if (r.getCode() == 0){      //表示查询订单请求无错误
                OrderVo order = (OrderVo) r.getData("order", new TypeReference<OrderVo>() {
                });
                if (order == null || order.getStatus() == 4){
                    //需要回滚订单    需要skuid  wareid  skunum
                    Long skuId = entity.getSkuId();
                    Long wareId = entity.getWareId();
                    Integer skuNum = entity.getSkuNum();
//  如果已被解锁，则拒签信息
                    if (entity.getLockStatus() == 1){
                        wareOrderTaskDetailService.rollbackMd(skuId,wareId,skuNum,detailId);     //回滚订单详情方法
                    }
                }else {
                //  这是订单状态为 已付款  不需要解锁
                }
            }
        }else {
            //主解锁库存消息已经将订单详情状态变为已解锁  无需解锁
        }
    }

    //解锁库存主消息 是由order服务创建订单成功后发来的
    @Transactional
    @Override
    public void unlockStock(OrderVo orderVo) {
        String orderSn = orderVo.getOrderSn();
        WareOrderTaskEntity one = wareOrderTaskService.getOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", orderSn));
        if (one != null){
            List<WareOrderTaskDetailEntity> list = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>().eq("task_id", one.getId())
                    .eq("lock_status",1));
            for (WareOrderTaskDetailEntity entity:list){
                if (entity.getLockStatus() == 1){
                    wareOrderTaskDetailService.rollbackMd(entity.getSkuId(), entity.getWareId(),entity.getSkuNum(),entity.getId());
                }
            }
        }
    }

    class WareSkuInfo {
        private List<Long> wareIds;
        private Long skuId;
        private Integer num;

        public List<Long> getWareIds() {
            return wareIds;
        }

        public void setWareIds(List<Long> wareIds) {
            this.wareIds = wareIds;
        }

        public Long getSkuId() {
            return skuId;
        }

        public void setSkuId(Long skuId) {
            this.skuId = skuId;
        }

        public Integer getNum() {
            return num;
        }

        public void setNum(Integer num) {
            this.num = num;
        }
    }
}
